package net

import (
	"fmt"
	flag "github.com/spf13/pflag"
	"go.uber.org/zap"
	"net"
	"net/url"
	"strconv"
	"strings"
)

// IsValidPort check if the port is legal. 0 is considered as a non valid port.
func IsValidPort(port int) bool {
	return port > 0 && port < 65535
}

// GetFreePort 获取当前主机空闲的端口
func GetFreePort() (int, error) {
	addr, err := net.ResolveTCPAddr("tcp", "localhost:0")
	if err != nil {
		return 0, err
	}

	l, err := net.ListenTCP("tcp", addr)
	if err != nil {
		return 0, err
	}
	defer l.Close()
	return l.Addr().(*net.TCPAddr).Port, nil
}

// GetCommandLineIpAndPort 获取命令行的ip和地址
func GetCommandLineIpAndPort(defaultIp string, defaultPort int) (string, int) {
	ip := flag.String("ip", defaultIp, "ip地址")
	port := flag.Int("port", defaultPort, "端口号")
	flag.Parse()
	return *ip, *port
}

// GenerateIpAndPort 生成默认的ip和地址
func GenerateIpAndPort(ipAddress *string, portAddress *int) {
	// 首先获取命令行的ip和port
	defaultIp := "0.0.0.0"
	defaultPort := 0
	commandIp, commandPort := GetCommandLineIpAndPort(defaultIp, defaultPort)
	zap.S().Infof("getCommandIp=%s,getCommandPort=%d", commandIp, commandPort)
	// 给配置的ip和地址赋值
	if commandIp != "" && commandIp != defaultIp {
		*ipAddress = commandIp
	}
	if commandPort != defaultPort {
		*portAddress = commandPort
	}
	// 如果配置文件和命令行都没有赋值，则配置默认值
	if *ipAddress == "" {
		*ipAddress = defaultIp
	}
	if *portAddress == 0 {
		freePort, err := GetFreePort()
		if err != nil {
			zap.S().Errorf("get free port error. %v", err)
		} else {
			zap.S().Infof("get free port=%d \n", freePort)
			*portAddress = freePort
		}
	}
}

// ExtractHostPort from address
func ExtractHostPort(addr string) (host string, port uint64, err error) {
	var ports string
	host, ports, err = net.SplitHostPort(addr)
	if err != nil {
		return
	}
	port, err = strconv.ParseUint(ports, 10, 16) //nolint:gomnd
	if err != nil {
		return
	}
	return
}

func IsValidIP(addr string) bool {
	ip := net.ParseIP(addr)
	return ip.IsGlobalUnicast() && !ip.IsInterfaceLocalMulticast()
}

// Port return a real port.
func Port(lis net.Listener) (int, bool) {
	if addr, ok := lis.Addr().(*net.TCPAddr); ok {
		return addr.Port, true
	}
	return 0, false
}

// Extract returns a private addr and port.
func Extract(hostPort string, lis net.Listener) (string, error) {
	addr, port, err := net.SplitHostPort(hostPort)
	if err != nil && lis == nil {
		return "", err
	}
	if lis != nil {
		if p, ok := Port(lis); ok {
			port = strconv.Itoa(p)
		} else {
			return "", fmt.Errorf("failed to extract port: %v", lis.Addr())
		}
	}
	if len(addr) > 0 && (addr != "0.0.0.0" && addr != "[::]" && addr != "::") {
		return net.JoinHostPort(addr, port), nil
	}
	ifaces, err := net.Interfaces()
	if err != nil {
		return "", err
	}
	lowest := int(^uint(0) >> 1)
	var result net.IP
	for _, iface := range ifaces {
		if (iface.Flags & net.FlagUp) == 0 {
			continue
		}
		if iface.Index < lowest || result == nil {
			lowest = iface.Index
		} else if result != nil {
			continue
		}
		addrs, err := iface.Addrs()
		if err != nil {
			continue
		}
		for _, rawAddr := range addrs {
			var ip net.IP
			switch addr := rawAddr.(type) {
			case *net.IPAddr:
				ip = addr.IP
			case *net.IPNet:
				ip = addr.IP
			default:
				continue
			}
			if IsValidIP(ip.String()) {
				result = ip
			}
		}
	}
	if result != nil {
		return net.JoinHostPort(result.String(), port), nil
	}
	return "", nil
}

func ParseHostAndPort(endpoint string) (host string, port string, err error) {

	// 如果没有没人的schema，默认添加http://
	if !strings.Contains(endpoint, "://") {
		endpoint = "http://" + endpoint
	}
	// 解析url
	parse, err := url.Parse(endpoint)
	if err != nil {
		return "", "", err
	}
	return parse.Hostname(), parse.Port(), nil
}
